package com.huika.dealontop.control.pay.view;

import android.content.Context;
import android.content.UriMatcher;
import android.net.Uri;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.View;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import com.huika.dealontop.utils.DebugTool;

import java.util.ArrayList;
import java.util.List;

/**
 * Author: Victor Wu <br/>
 * Email: victor-wu*foxmail.com <br/>
 * Time: 2015/12/31 10:36 <br/>
 * Description:
 *          可维护访问连接集合的WebView, 并可以在加载一个连接时，设置加载模式 <br/>
 *          url加载模式有4 种：<ul>
 *                <li>standard：标准模式（默认）,每加载一个 url,就添加到栈中;
 *                <li>singleTop：如果栈顶是当前要加载的url,就刷新栈顶 url，不重复添加到栈顶；
 *                <li>singleTask ：如果堆栈中已经存在当前要加载的 url，把这个url 之后的url从栈中移除，刷新这个 url；
 *                <li>singleInstance ：把堆栈清空，并将当前 url添加到栈底；
 *           </ul>
 */
public abstract class StackModeWebView extends WebView {
    private static final String TAG = StackModeWebView.class.getSimpleName();

    private ProxyWebViewClient mProxyWebViewClient;
    /**
     * 任务栈
     */
    private List<String> urlStack = new ArrayList<String>();
    /**
     * 保存 url加载模式
     */
    private UriMatcher mURIModeMatcher = new UriMatcher(LoadMode.STANDARD);

    /**
     * 栈底回调
     */
    private OnStackBottomListener onBottomListener;

    /**
     * 调用者设置的按键监听回调
     */
    private OnKeyListener onKeyListener;

    /**
     * url 加载模式 <br/>
     * 有 4种加载模式：<ul>
     *                <li>standard：标准模式（默认）,每加载一个 url,就添加到栈中;
     *                <li>singleTop：如果栈顶是当前要加载的url,就刷新栈顶 url，不重复添加到栈顶；
     *                <li>singleTask ：如果堆栈中已经存在当前要加载的 url，把这个url 之后的url从栈中移除，刷新这个 url；
     *                <li>singleInstance ：把堆栈清空，并将当前 url添加到栈底；
     *           </ul>
     *  <br/>
     *  调用 {@link StackModeWebView#addUrlLoadMode(java.lang.String, int)}方法设置某个 url的加载模式
     */
    public interface LoadMode{
        /**
         * 标准模式（默认）,每加载一个 url,就添加到栈中
         */
        int STANDARD = 0;
        /**
         * 如果栈顶是当前要加载的url,就刷新栈顶 url，不重复添加到栈顶
         */
        int SINGLE_TOP = 1;
        /**
         * 如果堆栈中已经存在当前要加载的 url，把这个url 之后的url从栈中移除，刷新这个 url
         */
        int SINGLE_TASK = 2;
        /**
         * 把堆栈清空，并将当前url添加到栈底
         */
        int SINGLE_INSTANCE = 3;
    }

    public StackModeWebView(Context context) {
        super (context);
        initializeStack();
    }

    public StackModeWebView(Context context, AttributeSet attrs) {
        super (context, attrs);
        initializeStack();
    }

    public StackModeWebView(Context context, AttributeSet attrs, int defStyle) {
        super (context, attrs, defStyle);
        initializeStack();
    }

    /**
     * 初始化
     */
    private void initializeStack() {
        mProxyWebViewClient = new ProxyWebViewClient();
        super .setWebViewClient(mProxyWebViewClient);
        super .setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (//event.getAction() == KeyEvent.ACTION_DOWN &&
                        keyCode == KeyEvent.KEYCODE_BACK) {
                    DebugTool. i(TAG, "onKey --> " + event.getAction() + " url size : " + urlStack .size());
                    if (event.getAction() == KeyEvent. ACTION_DOWN) {
                        int size = urlStack.size();
                        if (size > 1) {
                            //goBack();   //后退
                            //移除当前页面
                            urlStack.remove(size - 1);
                            //加载前一个页面
                            StackModeWebView.super.loadUrl(urlStack.get(size - 2));
                        } else {
                            onBottom();
                        }
                    }
                    if (onKeyListener != null) {
                        onKeyListener.onKey(v, keyCode, event);
                    }
                    return true;
                }

                if (onKeyListener != null) {
                    return onKeyListener .onKey(v, keyCode, event);
                }
                return false;
            }
        });
    }

    /**
     * 已经到栈底
     * @return
     */
    protected void onBottom() {
        if (onBottomListener != null){
            onBottomListener.onStackBottom();
        }
    }

    /**
     * 复写父类的 setWebViewClient(client) 方法，并把这个实现类 WebViewClient设置设置的代理类中
     */
    @Override
    public void setWebViewClient(WebViewClient client) {
        mProxyWebViewClient .setSubclassClient(client);
    }

    @Override
    public void setOnKeyListener(OnKeyListener l) {
        this .onKeyListener = l;
    }

    @Override
    public void loadUrl(String url) {
        int mode = getUrlLoadMode(url);
        DebugTool.i( TAG, "url = " + url + " mode = " + mode);
        if (LoadMode.STANDARD == mode){
            urlStack.add(url);
        }else if (LoadMode.SINGLE_TOP == mode){
            if(!getTopUrlOfStack().equals(url)){
                urlStack.add(url);
            }
        }else if (LoadMode.SINGLE_TASK == mode){
            int index = urlStack.indexOf(url);
            if(index == -1){
                urlStack.add(url);
            }else{
                for (int i = urlStack.size() - 1; i > index; i--) { //从栈顶开始逐个移除
                    urlStack.remove(i);
                }
            }
        }else if (LoadMode.SINGLE_INSTANCE == mode){
            urlStack.clear();
            urlStack.add(url);
        }
        printUrlStack();
        super .loadUrl(url);
    }

    private void printUrlStack() {
        DebugTool.i(TAG, "size : " + urlStack.size());
        for (int i = 0; i < urlStack.size(); i++) {
            DebugTool.i( TAG, "index : " + i + " --> " + urlStack.get(i));
        }
    }


    /**
     *
     */
    class ProxyWebViewClient extends WebViewClientWarp {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            WebView.HitTestResult hit = view.getHitTestResult();
            if (hit == null) {
                //当前这个 url属于前一个页面重定向过来的， 所以把前一个页面重历史堆栈中移除
                int last = urlStack.size() - 1;
                if (last >= 0) {
                    urlStack.remove(last);
                }
            }

            if(getSubclassClient() != null && getSubclassClient().shouldOverrideUrlLoading(view,url)){
                return true;
            }
            StackModeWebView.this.loadUrl(url);  //加载新的url
            return true;    //返回 true,代表事件已处理,事件流到此终止
        }
    }
    /* ************************************
     * 不适合用动态代理， WebViewClient不是一个接口类型,也没有实现任何接口
     *  改用静态代理 com.huika.tooktlook.webview.WebViewClientWarp  实现
     ***********************************/
    /*class WebViewClientInvocationHandler implements InvocationHandler {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if(mRealWebViewClient != null){
                if(method.getName() == "shouldOverrideUrlLoading"){
                    boolean b = (Boolean)method.invoke(mRealWebViewClient, args);
                    if(!b){
                        b = (Boolean)method.invoke(proxy, args);
                    }
                    return b;
                }
                return method.invoke(mRealWebViewClient, args);
            }else {
                return method.invoke(proxy, args);
            }
        }
    }*/

    /**
     *  添加一个 URL匹配的加载模式，务必在loadUrl(url)之前调用 ;<br/>
     *  URL节点可以精确的匹配字符串，也可以使用标记 “*”匹配任何文本,或使用标记 “#”相匹配数字。
     *  <p/>
     * @param url 需要指定加载模式的 URL， URL节点可以精确的匹配字符串，
     *             也可以使用标记 “*”匹配任何文本,或使用标记 “#”相匹配数字。
     * @param mode 加载模式
     *         { @link LoadMode#STANDARD},
     *         {@link LoadMode#SINGLE_TOP} or
     *         {@link LoadMode#SINGLE_TASK} or
     *         {@link LoadMode#SINGLE_INSTANCE}
     */
    public void addUrlLoadMode(String url, int mode){
        Uri uri = Uri.parse(url);
        String authority = uri.getAuthority();
        String path = uri.getPath();
        mURIModeMatcher .addURI(authority,
                TextUtils.isEmpty(path) || "/".equals(path) ? null : path ,
                mode);
    }

    /**
     * 获取 url 的加载模式
     * @param url
     * @return 通过 addUrlLoadMode() 添加的加载模式
     *         { @link LoadMode#STANDARD},
     *         {@link LoadMode#SINGLE_TOP} or
     *         {@link LoadMode#SINGLE_TASK} or
     *         {@link LoadMode#SINGLE_INSTANCE} ,
     *         如果该url 没有设置任何加载模式，将返回默认的加载模式 {@link LoadMode#STANDARD}
     */
    public int getUrlLoadMode(String url){
        int mode = mURIModeMatcher.match(Uri.parse(url));
        if (mode >= 0 ) return mode;
        return LoadMode.STANDARD;
    }

    /**
     * 获取任务栈
     * @return
     */
    public List<String> getUrlStack() {
        return urlStack;
    }

    /**
     * 根据下标，获取栈中的 url
     * @param index
     * @return
     */
    public String getUrlByStackIndex(int index){
        if (index < 0 || index >= getStackSize()){
            return null;
        }
        return urlStack.get(index);
    }

    /**
     * 获取栈顶位置
     * @return
     */
    public int getTopUrlIndexOfStack(){
        return getStackSize() - 1 ;
    }

    /**
     * 获取栈顶的 url
     * @return
     */
    public String getTopUrlOfStack(){
        int top = getTopUrlIndexOfStack();
        if (top >= 0 ){
            return urlStack.get(top);
        }
        return null ;
    }

    /**
     * @return
     */
    public int getStackSize(){
        return urlStack.size();
    }

    /**
     * 设置栈底监听
     * @param onBottomListener
     */
    public void setOnBottomListener(OnStackBottomListener onBottomListener) {
        this .onBottomListener = onBottomListener;
    }

}
